package com.sunrise.gateway.shiro.security;

import com.sunrise.gateway.commons.CommonUtils;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.subject.SubjectContext;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.session.mgt.WebSessionKey;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.util.StringUtils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MySecurityManager extends DefaultWebSecurityManager {

	private String resolveUriPathSegmentParam(String uri, String paramName) {
		int queryStartIndex = uri.indexOf('?');
		if (queryStartIndex >= 0) { // get rid of the query string
			uri = uri.substring(0, queryStartIndex);
		}

		int index = uri.indexOf(';'); // now check for path segment parameters:
		if (index < 0) {
			// no path segment params - return:
			return null;
		}

		// there are path segment params, let's get the last one that may exist:

		final String TOKEN = paramName + "=";

		uri = uri.substring(index + 1); // uri now contains only the path
		// segment params

		// we only care about the last JSESSIONID param:
		index = uri.lastIndexOf(TOKEN);
		if (index < 0) {
			// no segment param:
			return null;
		}

		uri = uri.substring(index + TOKEN.length());

		index = uri.indexOf(';'); // strip off any remaining segment params:
		if (index >= 0) {
			uri = uri.substring(0, index);
		}

		return uri; // what remains is the value
	}
	private String resolveParamFromUrl(HttpServletRequest request, String paramName) {
		String value = request.getParameter(paramName);
		if (!StringUtils.isEmpty(value)) {
			return value;
		}

		String uri = request.getRequestURI();
		if (uri == null) {
			return null;
		}
		return resolveUriPathSegmentParam(uri, paramName);
	}


	private static void addSessionCookie(HttpServletRequest req, HttpServletResponse resp,
			String sessionIdName, String sessionId, int sessionMaxAge) {
		SimpleCookie sc = new SimpleCookie();
		sc.setName(sessionIdName);
		sc.setValue(sessionId);
		sc.setHttpOnly(true);
		sc.setDomain(CommonUtils.resolveDomain(req));
		sc.setPath("/");
		sc.setMaxAge(Integer.MAX_VALUE);
		sc.saveTo(req, resp);
	}

	private org.apache.shiro.web.servlet.Cookie sessionIdCookie;
	private org.apache.shiro.web.servlet.Cookie resolveSessionIdCookie() {
		if (null == sessionIdCookie) {
			DefaultWebSessionManager sm = (DefaultWebSessionManager) getSessionManager();
			if (sm != null) {
				sessionIdCookie = sm.getSessionIdCookie();
			}
		}
		return sessionIdCookie;
	}
	private String resolveSessionIdName() {
		org.apache.shiro.web.servlet.Cookie sessionIdCookie = resolveSessionIdCookie();
		if (null != sessionIdCookie) {
			return sessionIdCookie.getName();
		} else {
			return "JSESSIONID";
		}
	}
	private int resolveSessionMaxAge() {
		org.apache.shiro.web.servlet.Cookie sessionIdCookie = resolveSessionIdCookie();
		if (null != sessionIdCookie) {
			return sessionIdCookie.getMaxAge();
		} else {
			return 1209600;
		}
	}

	private String resolveSessionIdFromUrl(HttpServletRequest req, HttpServletResponse resp) {
		final String sessionIdName = resolveSessionIdName();

		String sessionId = resolveParamFromUrl(req, sessionIdName);
		if (sessionId != null) {
			addSessionCookie(req, resp, sessionIdName, sessionId, resolveSessionMaxAge());
			return sessionId;
		}

		return null;
	}

	private final static Pattern sessionIdPattern = Pattern.compile("^[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}$");
	private static boolean isValidSessionId(String sessionId) {
		if (null == sessionId) {
			return false;
		}
		Matcher m = sessionIdPattern.matcher(sessionId);
		return m.matches();
	}
	private static String resolveSessionIdFromCookie(HttpServletRequest req, String sessionIdName) {
		Cookie[] oldCookies = req.getCookies();
		if (null == oldCookies || oldCookies.length <= 0) {
			return null;
		}

		for (int i=oldCookies.length-1; i>=0 ;--i) {
			Cookie oldCookie = oldCookies[i];
			if (sessionIdName.equals(oldCookie.getName()) && oldCookie.getMaxAge() != 0) {
				if (isValidSessionId(oldCookie.getValue())) {
					return oldCookie.getValue();
				} else {
					return null;
				}
			}
		}

		return null;
	}


	// 如果sessionId 为空, 从url的参数获取它
	@Override
	protected SessionKey getSessionKey(SubjectContext context) {
		if (WebUtils.isWeb(context)) {
			HttpServletRequest request = (HttpServletRequest) WebUtils.getRequest(context);
			HttpServletResponse response = (HttpServletResponse) WebUtils.getResponse(context);
			String sessionId = resolveSessionIdFromUrl(request, response);
			if (!StringUtils.isEmpty(sessionId)) {
				context.setSessionId(sessionId);
			} else {
				sessionId = resolveSessionIdFromCookie(request, resolveSessionIdName());
				if (!StringUtils.isEmpty(sessionId)) {
					context.setSessionId(sessionId);
				}
			}
			return new WebSessionKey(sessionId, request, response);
		} else {
			return super.getSessionKey(context);
		}
	}

}
